/**
  ETFAna project, Anyang Normal University && IMP-CAS
  \class ETFRun
  \brief The main analysis run class. A singleton class.
  \author SUN Yazhou, asia.rabbit@163.com
  \since 2024-06-12
  \date 2024-06-19 last modified
  \attention
  changelog
  <table>
  <tr>  <th>Date         <th>Author      <th>Description                   </tr>
  <tr>  <td>2024-06-12   <td>Asia Sun    <td>file created                  </tr>
  </table>

  \copyright Copyright (c) 2021-2024 Anyang Normal U. && IMP-CAS with LGPLv3 LICENSE
*/

#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <iostream>
#include <TTree.h>
#include <TFile.h>
#include <TSystem.h>
#include <TApplication.h>

#include "ETFRun.h"
#include "ETFSource.h"
#include "ETFOfflineSource.h"
#include "ETFOnlineSource.h"
#include "ETFGOnline.h"
#include "ETFOnlineCache.h"
#include "ETFParaManager.h"
#include "ETFAnalyzer.h"
#include "ETFGanaPre.h"
#include "ETFMsg.h"

using std::cout;
using std::endl;
using std::flush;
using std::mutex;

#define er ETFMsg::Error

static void handler_ctrlc(int sig, siginfo_t *info, void *context){
  ETFMsg::SetIRP();
} // end function handlerr_ctrlc

ETFRun *ETFRun::kInstance = nullptr;
mutex ETFRun::kMutex;

ETFRun::ETFRun() : fRootFile(0), fTree(0), fSrc(0), fTasks(0), fIsOnline(false){}

ETFRun *ETFRun::Instance(){
  if(!kInstance) kInstance = new ETFRun();
  return kInstance;
} // end member function Instance

ETFRun::~ETFRun(){}

void ETFRun::Init(){
  fpm = ETFParaManager::Instance();
  if(fIsOnline) OnlineInit();
  else OfflineInit();
  fRootFile = new TFile(fSrc->ROOTFileName(), "RECREATE");
  fRootFile->cd();
  fTree = new TTree("etf", "External Target Facility, HIRFL-CSR");

  fSrc->Init();
  fAnalyzer = new ETFAnalyzer();
  fAnalyzer->Init(); // det constructions
  fAnalyzer->Register(); // register all the branches to the out tree
  fpm->PrintUserInput();

  for(auto &t : fTasks) t->Init(); // histo and tree constructions
} // end member function Init

void ETFRun::OnlineSim(int rest_us){
  ETFOfflineSource *s = dynamic_cast<ETFOfflineSource *>(fSrc);
  if(!s) er("ETFRun", "SetOnline: nullptr offline source");
  if(rest_us >= 0) s->OnlineSim(rest_us);
} // end member function SetOnline

void ETFRun::OfflineInit(){
  ETFOfflineSource *src = new ETFOfflineSource();
  fpm->ConfigOfflineSource(src);
  fSrc = src;
} // end member function OfflineInit

void ETFRun::OnlineInit(){
  ETFOnlineSource *src = new ETFOnlineSource();
  fpm->ConfigOnlineSource(src);
  src->Online(); // start the reading threads
  src->SetClean(true); // if removing the generated rootfile
  fSrc = src;

  // visualize the GUI //
	fGOnline = new ETFGOnline(1200, 800); // the main GUI frame
  fGOnline->SetWindowName("GANA Online - GUI Data Analysis Framework for ETF Online");
  fGOnline->Initialize(); // arrange for the new widgets and map windows
  fGOnline->Configure(); // show on the screen
} // end member function OnlineInit

void ETFRun::Run(unsigned ev0, unsigned ev1){
  ETFMsg::ms1(); // start timing
  if(fIsOnline) OnlineRun();
  else OfflineRun(ev0, ev1);
  Finish();
  Close();
} // end member function Run

void ETFRun::OfflineRun(unsigned ev0, unsigned ev1){
  // taking care of SIGINT signal
  struct sigaction sa; sa.sa_sigaction = handler_ctrlc;
  for(unsigned i = ev0; i < ev1; i++){
    gSystem->IgnoreInterrupt();
    sigaction(SIGINT, &sa, nullptr);
    if(ETFMsg::irp()) break;
    if(!fSrc->ReadEvent(i)) break; // read and unpack
    fAnalyzer->Analyze(); // map chs to dets, and trking & PID-ing
    fTree->Fill();
    for(auto &t : fTasks) t->Exec(); // fill some histos
    if(i%5000 == 0)
    // if(1) // DEBUG
    {
      fAnalyzer->PrintPerEvent();
      if(fGOffline) fGOffline->Update();
    } // end if
    if(fGOffline) gSystem->ProcessEvents();
    // if(ETFMsg::ms1() >= 1000) break; // DEBUG
    // cout << "Processing event " << i << '\r' << flush; // DEBUG
  } // end for over i
  fAnalyzer->PrintPerEvent();
  if(fGOffline) fGOffline->Update();
} // end member function OfflineRun

void ETFRun::OnlineRun(){
  ETFOnlineSource *src = dynamic_cast<ETFOnlineSource *>(fSrc);
  time_t t0 = ETFMsg::ms(), t1 = t0; // timing starts
  struct sigaction sa; sa.sa_sigaction = handler_ctrlc;
  while(!ETFMsg::irp()){
    gSystem->IgnoreInterrupt();
    sigaction(SIGINT, &sa, nullptr);
    if(src->ReadEvent()){
      fAnalyzer->Analyze();
      Lock();
      fTree->Fill();
      for(auto &t : fTasks) t->Exec(); // fill some histos
      Unlock();
      if(ETFMsg::ms() - t1 > fGOnline->UpdateDTInMS()){
        t1 = ETFMsg::ms();
        fGOnline->Update();
      } // end if
    } // end if
    else usleep(5);
    gSystem->ProcessEvents();
    usleep(50); // may help in dealing with data race
    // show the ana results //
    if(ETFMsg::ms() - t0 > 1000){ // print per sec
      t0 = ETFMsg::ms();
      fAnalyzer->PrintPerEventOnline();
    } // end if
  } // end while
} // end member function OnlineRun

void ETFRun::Finish(){
  fSrc->PrintSummary();
  fAnalyzer->PrintSummary();
  fRootFile->cd();
  if(!fIsOnline || !fSrc->IsClean()){
    for(auto &t : fTasks) t->WriteHistos();
    fTree->Write("", TObject::kOverwrite);
    fRootFile->Close();
  } // end if
  ETFMsg::Logo();
} // end member function Finish

void ETFRun::Close(){
  ETFMsg::Info("ETFRun", "--- Close: ETFAna is CLOSING... Please wait... ---");
  delete fSrc;
  for(auto &t : fTasks) delete t;
  delete fpm;
  cout << "\033[32;1mETFRun has CLOSED.  Bye :)\033[0m" << endl;
  cout << "Time used for event ana loop: " << ETFMsg::ms1() / 1000. << "s" << endl;
  if(fIsOnline) gApplication->Terminate();
} // end member function Close

unsigned ETFRun::Index() const{ return fAnalyzer->Index(); }
unsigned ETFRun::CntEv() const{ return fAnalyzer->CntEv(); }
const char *ETFRun::GetRootFileName(){
  if(!fRootFile) er("ETFRun", "GetRootFileName: fRootFile is nullptr");
  return fRootFile->GetName();
} // end member function GetRootFileName

// remove the generated rootfile
void ETFRun::SetOnlineClean(){
  if(fIsOnline && fSrc) static_cast<ETFOnlineSource *>(fSrc)->SetClean();
} // end member function SetOnlineClean
